#pragma once

#include<iostream>
#include<string>
#include<cstring>
#include<sys/types.h>
#include<unistd.h>
#include<stdarg.h>
#include<ctime>
#include<fstream>
#include<pthread.h>
#include<cstdio>
#include"LockGuard.hpp"

using std::cin;
using std::cout;
using std::endl;

namespace log_ns
{

    // 日志等级
    enum
    {
        DEBUG=1,
        INFO,
        WARING,
        ERROR,
        FATAL
    };

    // 日志消息
    class logmessage
    {

    public:
        std::string _level; // 日志等级
        pid_t _id; 
        std::string _filename; // 文件名
        int _filenumber;  // 行号
        std::string _cur_time;
        std::string _message_info;

    };

    pthread_mutex_t glock=PTHREAD_MUTEX_INITIALIZER; // 定义一个全局的锁

    std::string LevelToString(int level)
    {
        LockGuard lockguard(&glock);
        switch(level)
        {
            case DEBUG:
                return "DEBUG";
            case INFO:
                return "INFO";
            case WARING:
                return "WARING";
            case ERROR:
                return "ERROR";
            case FATAL:
                return "FATAL";
            default:
                return "UNKNOWN";
        }
    }

    std::string GetCurTime()
    {
        time_t now=time(nullptr);

        struct tm* cur_time=localtime(&now);
        char buffer[128];
        snprintf(buffer,sizeof(buffer),"%d-%02d-%02d %02d:%02d:%02d",
                cur_time->tm_year+1900,
                cur_time->tm_mon+1,
                cur_time->tm_mday,
                cur_time->tm_hour,
                cur_time->tm_min,
                cur_time->tm_sec);
        return std::string(buffer);
    }

    #define SCREEN_TYPE 1
    #define FILE_TYPE 2

    const std::string glogfile="./log.txt";
    // 日志
    class Log
    {
    public:
        Log(const std::string& logfeile=glogfile):_logfile(logfeile),_type(SCREEN_TYPE)
        {

        }

        void Enable(int type)
        {
            _type=type;
        }

        void FlushLogToSCreen(const logmessage& lg)
        {
            printf("[%s][%d][%s][%d][%s] %s",
                lg._level.c_str(),
                lg._id,
                lg._filename.c_str(),
                lg._filenumber,
                lg._cur_time.c_str(),
                lg._message_info.c_str()
            );
        }

        void FlushLogToFile(const logmessage& lg)
        {
            std::ofstream out(_logfile,std::ios::app); // 追加打印
            if(!out.is_open())
                return;

            char logtxt[2048];
            snprintf(logtxt,sizeof(logtxt),"[%s][%d][%s][%d][%s] %s",
                lg._level.c_str(),
                lg._id,
                lg._filename.c_str(),
                lg._filenumber,
                lg._cur_time.c_str(),
                lg._message_info.c_str()
            );

            out.write(logtxt,strlen(logtxt));
            out.close();
        }

        void FlushLog(const logmessage& lg)
        {

            switch(_type)
            {
                case SCREEN_TYPE:
                    FlushLogToSCreen(lg);
                    break;
                case FILE_TYPE:
                    FlushLogToFile(lg);
                    break;
            }
        }

        void logMessage(std::string filename,int filenumber,int level,const char* format,...)
        {
            logmessage lg;
            lg._level=LevelToString(level);
            lg._id=getpid();
            lg._filename=filename;
            lg._filenumber=filenumber;
            lg._cur_time=GetCurTime();

            va_list ap;
            va_start(ap,format);
            char log_info[1024];
            vsnprintf(log_info,sizeof(log_info),format,ap);
            va_end(ap);
            
            lg._message_info=log_info;

            //cout<<lg._message_info<<endl;
            // 打印日志
            FlushLog(lg);

        }

        ~Log()
        {

        }

    private:
        int _type;
        std::string _logfile;
    };

    Log lg;

    #define LOG(Level,Format,...) do{lg.logMessage(__FILE__,__LINE__,Level,Format,##__VA_ARGS__);}while(0)
    #define EnableScreen() do{lg.Enable(SCREEN_TYPE);}while(0)
    #define EnableFile() do{lg.Enable(FILE_TYPE);}while(0)

};